﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EvoAlgLib
{
    public class OrderCrossover : Crossover
    {
        private double p_cross;
        private IndFactory indFactory;

        public OrderCrossover(double p_cross, IndFactory indFactory)
        {
            this.p_cross = p_cross;
            this.indFactory = indFactory;
        }
        public List<Individual> cross(List<Individual> parents)
        {
            List<Individual> ans = new List<Individual>();
            if (LibStatics.getRandomizer().NextDouble() > this.p_cross)
            {
                foreach (Individual parent in parents)
                    ans.Add(this.indFactory.getIndividual(parent));
            }
            else
            {
                if (parents.Count == 2)
                {
                    
                    int length = ((int[])parents[1].getGenotype()).Length;

                    Individual offspring0 = this.indFactory.getIndividual(parents[0]);
                    Individual offspring1 = this.indFactory.getIndividual(parents[1]);
                    int p1 = LibStatics.getRandomizer().Next(length);
                    int p2 = LibStatics.getRandomizer().Next(length);

                    HashSet<int> used = new HashSet<int>();
                    for (int i = p1; i < p2; ++i)
                    {
                        int x = ((int[])parents[0].getGenotype())[i];
                        ((int[])offspring0.getGenotype())[i] = x;
                        used.Add(x);
                    }
                    int p = p2;
                    for (int i = p2; i != p1; i = (i + 1) % length)
                    {
                        int x = ((int[])parents[1].getGenotype())[i];
                        if (used.Contains(x))
                            continue;
                        ((int[])offspring0.getGenotype())[p] = x;
                        p = (p + 1) % length;
                    }



                    used.Clear();
                    for (int i = p1; i < p2; ++i)
                    {
                        int x = ((int[])parents[1].getGenotype())[i];
                        ((int[])offspring1.getGenotype())[i] = x;
                        used.Add(x);
                    }
                    p = p2;
                    for (int i = p2; i != p1; i = (i + 1) % length)
                    {
                        int x = ((int[])parents[0].getGenotype())[i];
                        if (used.Contains(x))
                            continue;
                        ((int[])offspring1.getGenotype())[p] = x;
                        p = (p+1) % length;
                    }

                    //if crossover accured => mutate
                    offspring0.mutate();
                    offspring1.mutate();

                    ans.Add(offspring0);
                    ans.Add(offspring1);
                }
                else
                {
                    throw new System.ArgumentException("List must contain exactly 2 Individual", "original");
                }

            }
            return ans;
        }
    }
}
